import Emitter from '../utils/Emitter.js';
import { setResources } from '../core/api.js';
import KernelWorker from '../../workers/kernel.worker.js';
/**
 * Kernel for 
 * @author wds
 * @date 2022.12.26
 */
let kernelInstance = null;
class Kernel {
    constructor(root) {
        let now = Date.now();
        this.name = '@Kernel' + now;
        this.$root = root;
        this.lifeCyclePeriod = 100;
        this.updatesPool = [];
        this.currentPage = '';
        this.KernelWorker = null;
        this.disconnecting = false;
        // this._emitter = Emitter.getInstance();
        this._emitter = new Emitter();
        // console.warn('[Kernel] constructor: ', now, this, ...arguments);
    }

    static getInstance(root) {
        if (!kernelInstance) kernelInstance = new Kernel(root);
        return kernelInstance;
    }

    // emitter
    on(event, callback) {
        this._emitter.on(Emitter.getName(event, this.name), callback, this);
    }
    off(event, callback) {
        this._emitter.off(Emitter.getName(event, this.name), callback);
    }
    once(event, callback) {
        this._emitter.once(Emitter.getName(event, this.name), callback);
    }
    emit(event, ...args) {
        this._emitter.emit(Emitter.getName(event, this.name), ...args);
    }

    // initHandleWsFunc(cb) {
    //     this.handleWsFunc = cb;
    // }
    // initHandleResourcesFunc(cb) {
    //     this.handleResourcesFunc = cb;
    // }
    // initHandlePanicFunc(cb) {
    //     this.handlePanicFunc = cb;
    // }
    // initHandleOfflineFunc(cb) {
    //     this.handleOfflineFunc = cb;
    // }
    // initHandleIncrementFunc(cb) {
    //     this.handleIncrementFunc = cb;
    // }

    initWorker(k) {
        // if (k) this.KernelWorker = k;
        // console.warn('[Kernel] initWorker: ', KernelWorker, this.KernelWorker);
        // this.worker = this.KernelWorker ? new this.KernelWorker() : new KernelWorker();
        if (k) this.KernelWorker = k;
        // console.warn('[Kernel] initWorker: ', k, this.KernelWorker);
        if (!k && !this.KernelWorker) return console.error('[Kernel] initWorker: Please init worker file first!');
        this.worker = new this.KernelWorker();
        this.worker.onmessage = msg => {
            let res = msg.data;
            // console.warn('[kernel] worker.onmessage: ', res);
            if (res.task === 'ws') this.handleWs(res.data);
            else if (res.task === 'resources') this.handleResources.call(this, res);
            else if (res.task === 'increment') this.handleIncrement.call(this, res);
            else if (res.task === 'create') this.handleCreate.call(this, res.dataKay, res.data);
            else if (res.task === 'update') this.handleUpdate.call(this, res.dataKay, res.data, res.dataIndex);
            else if (res.task === 'panic') this.handlePanic.call(this, res.data);
            else if (res.task === 'offline') this.handleOffline.call(this, res.data);
        };
    }

    // Offline connect, use for login page or demo preview.
    offlineConnect(name, virtualVehicles, resources, randomStatusEnable) {
        // console.warn('[Kernel] offlineConnect: ', this);
        this.disconnect();
        this.initWorker();
        let params = {
            offline: name,
            virtualVehicles,
            resources,
            randomStatusEnable
        };
        this.worker.postMessage(params);
        this.disconnecting = false;
    }

    connect(token, url, subscriptions, lifeCyclePeriod) {
        if (!token) return;
        // console.warn('[kernel] connect...', ...arguments);

        this.token = token;
        if (url) this.url = url;
        if (subscriptions) this.subscriptions = subscriptions;
        if (lifeCyclePeriod) this.lifeCyclePeriod = lifeCyclePeriod;

        // if (this.worker) return this.reconnect(token);
        if (this.worker) this.disconnect();
        this.initWorker();

        let params = {
            task: 'connect',
            token: this.token,
            url: this.url,
            subscriptions: this.subscriptions,
            lifeCyclePeriod: this.lifeCyclePeriod
        };
        console.warn('[kernel] connect...', params);
        this.worker.postMessage(params);
        this.disconnecting = false;
    }

    reconnect(token) {
        console.warn('[kernel] reconnect...');
        this.disconnect();
        this.connect(token || this.token);
    }

    disconnect() {
        if (this.worker) {
            this.disconnecting = true;
            this.worker.postMessage({ task: 'terminate' });
            this.worker.terminate();
            this.worker = null;
        }
        if (this.$root) this.$root.resources = {};
    }

    postMessage(params) {
        if (!this.worker) return;
        this.worker.postMessage(params);
    }
    send(msg) {
        if (!this.worker) return;
        this.worker.postMessage({ task: 'send', data: msg });
    }

    // handle funcs
    handleWs(res) {
        // console.warn('[kernel] handleWs: ', res.data);
        this.$root.ws = res.ws;
        // if (typeof this.handleWsFunc === 'function') this.handleWsFunc(res);
        this.emit('handleWs', res);
    }

    handleResources(res) {
        // console.warn('[kernel] handleResources: ', this.disconnecting, res.data);
        if (this.disconnecting) return;
        this.$root.resources = res.data;
        setResources(this.$root.resources);
        // if (typeof this.handleResourcesFunc === 'function') this.handleResourcesFunc(res);
        this.emit('handleResources', res);
    }
    handleIncrement(res) {
        // console.warn('[Kernel] handleIncrement: ', this.disconnecting, res.data, this.$root.resources.models);
        if (this.disconnecting) return;
        Object.keys(res.data).forEach(k => {
            if (!this.$root.resources[k]) this.$root.resources[k] = res.data[k];
            if (res.data[k]) this.$root.resources[k] = res.data[k];
        });
        // if (res.refreshTypes && Object.keys(res.refreshTypes).length) console.error(res.refreshTypes);
        if (res.refreshTypes && Object.keys(res.refreshTypes).length) this.updatesPool.push(res.refreshTypes);
        if (this.updatesPool.length > 20) this.updatesPool.splice(0, 1);
        // if (typeof this.handleIncrementFunc === 'function') this.handleIncrementFunc(res);
        this.emit('handleIncrement', res);
        // console.warn('[kernel] handleResources: ', res.data, this.$root.resources);
    }
    handleCreate(key, val) {
        // console.log('[kernel] handleCreate: ', res);
        if (!this.$root.resources[key]) this.$root.resources[key] = [];
        this.$root.resources[key].push(val);
    }
    handleUpdate(key, val, idx) {
        // console.log('[kernel] handleUpdate: ', res);
        for (let p in val) {
            this.$root.resources[key][idx][p] = val[p];
        }
    }
    handlePanic(res) {
        console.warn('[kernel] handlePanic: ', res);
        // if (typeof this.handlePanicFunc === 'function') this.handlePanicFunc(res);
        this.emit('handlePanic', res);
    }
    handleOffline(res) { //eslint-disable-line
        console.warn('[kernel] offline: ', res);
        this.disconnect();
        // if (typeof this.handleOfflineFunc === 'function') this.handleOfflineFunc(res);
        this.emit('handleOffline', res);
    }
}
export default Kernel;
